#include "../logs/log.h"
#include <chrono>
#include <algorithm>
/*
    测试一个项目需要涉及到 ： 1、测试环境 2、测试方法 3、测试结果
    性能测试方法：
        每秒能打印⽇志数 = 打印⽇志条数 / 总的打印⽇志消耗时间
    测试工具的编写：
    1.可以控制写日志的线程数量
    2.可以控制写日志的总数量
    分别对于同步日志器&异步日志器进行各自的性能测试
        需要测试单写日志线程的性能
        需要测试多写日志线程的性能
    实现：
        封装一个接口，传入日志器名称，线程数量，日志数量，单挑日志大小
        在接口内，创建指定的线程，各自负责一部分日志的输出
        在输出之前计时开始，在输出完毕后计时结束 所耗时间 = 结束 - 开始
        每秒输出量 = 日志数量 / 总耗时
        每秒输出大小 = 日志数量*单条日志大小/总耗时
        ！异步日志输出使用非安全模式，纯内存写入（不考虑实际落地时间）
*/

void bench(const std::string& name,int sum_thread,int sum_log,size_t size_log)
{
    //获得日志器
    Hxy_Log::Logger::ptr logger = Hxy_Log::getLogger(name);
    if(logger.get() == nullptr) return;
    std::cout<<"测试日志: "<<sum_log<<"条，大小："<<(sum_log*size_log) / 1024<<"KB\n";
    std::string str(size_log,'A');  //构建一个大小为size_log长度的字符串
    int n = sum_log / sum_thread;   //每个线程需要输出的条数
    //创建指定线程
    std::vector<std::thread> threads;
    std::vector<double> cost_arry(sum_thread);
    for(int i = 0;i<sum_thread;i++)
    {
        //emplace_back 会进行创建对象并插入尾部
        threads.emplace_back([&,i](){
            auto start = std::chrono::high_resolution_clock::now(); // 获得开始时间
            for(int j = 0;j<n;j++)  //开始写日志
                logger->debug("%s",str.c_str());
            auto end = std::chrono::high_resolution_clock::now(); // 获得结束时间
            std::chrono::duration<double> cost = end - start;
            cost_arry[i] = cost.count();   //将每个线程所消耗的时间放进数组
            std::cout<<"\t线程 : "<<i<<"\t输出的数量: "<<n<<", 耗时: "<<cost.count()<<std::endl;
        });
    }
    
    for(int i = 0;i<sum_thread;i++)
    {
        threads[i].join();
    }
    //计算总耗时 -- 在多线程中，每个线程都是在cpu中并发执行的，只需要统计耗时最高的线程时间就是所以线程执行完的时间
    double total_time = INT8_MIN;
    for(int i = 0;i<sum_thread;i++) total_time = std::max(total_time,cost_arry[i]);
    size_t msg_pro_sec = sum_log / total_time;
    size_t size_pro_sec = (sum_log*size_log)/(total_time*1024);
    //打印
    std::cout<<"\t总耗时："<<total_time<<"s\n";
    std::cout<<"\t每秒输出的日志数量："<<msg_pro_sec<<"条\n";
    std::cout<<"\t每秒输出的日志大小: "<<size_pro_sec<<"KB\n";
}

void sync()
{
    std::unique_ptr<Hxy_Log::LoggerBuilder> logger(new Hxy_Log::GlobalLoggerBuilder());
    logger->BuildLoggerName("sync");
    logger->BuildLoggerType(Hxy_Log::LoggerType::SYNCTYPE);
    logger->BuildLoggerValue(Hxy_Log::LogLevel::value::DEBUG);
    logger->BuildFormatter();
    logger->BuildSink<Hxy_Log::FileSink>("./logfile/sync.log");
    logger->build();

    bench("sync",5,1000000,100);
}

void async()
{
    std::unique_ptr<Hxy_Log::LoggerBuilder> logger(new Hxy_Log::GlobalLoggerBuilder());
    logger->BuildLoggerName("async");
    logger->BuildLoggerType(Hxy_Log::LoggerType::ASYNCTYPE);
    logger->BuildLoggerValue(Hxy_Log::LogLevel::value::DEBUG);
    logger->BuildLoopTypeUnSafe();//开启非安全模式
    logger->BuildFormatter();
    logger->BuildSink<Hxy_Log::FileSink>("./logfile/async.log");
    logger->build();

    bench("async",10,1000000,100);
}

int main()
{
    //单线程下同步写日志可能会比多线程同步写日志快 -- 多线程在同步写日志的情况下会产生锁冲突，单线程是串行化过程，同步的情况下主要考验的是硬盘的写入性能
    //异步单线程写日志没有同步单线程快 -- 因为单线程异步会出现写一下拿一下的情况，此时缓冲区日志调试很少
    //异步多线程写日志只需要往缓存区写入，性能更考验cpu的处理能力，不再考了磁盘的写入性能 因此异步多线程提升更加明显
    async();
    //结果 同步下的单线程和多线程的区别 --  异步下单线程和多线程
    return 0;
}